#include <Kernel/GlobalData.h>
#include <Memory/Heap.h>
#include <Utility/CLib.h>
#include <ProcessThread/ProcessThread.h>
#include <Utility/intrinsic.h>

static inline void AddPCBToTree(LPProcessControlBlock lpParent, LPProcessControlBlock lpChild)
{
    lpChild->lpSiblingProcess = lpParent->lpChildProcess; /* 子进程主动链接其兄弟 */
    lpParent->lpChildProcess = lpChild; /* 父进程链接到新的子进程，由子进程逐个找到其兄弟 */
}

LPProcessControlBlock SimpleCreateProcess
(
    LPCSTR lpApplicationName,
    LPCSTR lpCommandLine,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment
)
{
    LPProcessControlBlock lpNewProcess = HeapAllocate(g_lpKernelHeap, HEAP_ZERO_MEMORY, sizeof(ProcessControlBlock)); /* 创建 PCB */
    lpNewProcess->lpszName = HeapAllocate(g_lpKernelHeap, 0, strlen(lpApplicationName));
    strcpy(lpNewProcess->lpszName, lpApplicationName);
    lpNewProcess->lpszExecPath = HeapAllocate(g_lpKernelHeap, 0, strlen(lpApplicationName));
    strcpy(lpNewProcess->lpszExecPath, lpApplicationName);
    /* 获取当前线程 */
    /* 创建进程属于操作系统功能，因此必然在内核态完成 */
    /* 通过当前栈的栈底可访问到 TCB */
    LPThreadControlBlock lpCurrentThread = (LPVOID)((DWORD)&lpCurrentThread & 0xFFFFE000);
    KERNEL_ASSERT(lpCurrentThread->lpProcess, "Thread is not possessed by any process. lpCurrentThread = %x.\r\n", lpCurrentThread);
    AddPCBToTree(lpCurrentThread->lpProcess, lpNewProcess); /* 新创建的进程成为当前进程的子进程 */
    /* 共享 system 页目录，推迟至主线程执行时再分配单独的页目录 */
    LPPageDirectoryEntry lpPageDirectory = AllocatePageForKernel(1, PTE_WRITEABLE);
    memset(lpPageDirectory, 0, 768 * sizeof(PageDirectoryEntry));
    for (int i = 768; i < 1023; i++)
    {
        lpPageDirectory[i] = g_MasterPageTable[1022][i];
    }
    lpNewProcess->dwCR3 = g_MasterPageTable[RESOLVE_PDE_INDEX(lpPageDirectory)][RESOLVE_PTE_INDEX(lpPageDirectory)] & 0xFFFFF000; /* 页目录物理地址 */
    lpPageDirectory[1023] = lpNewProcess->dwCR3 | PDE_PRESENT; /* 页目录自映射 */
    /* 为新进程创建主线程，新线程被调度运行时开始处于内核态，自行完成初始化工作 */
    LPThreadControlBlock lpMainThread = AllocateStackForKernel();
    /* 分配内核栈 */
    KERNEL_ASSERT(lpMainThread != NULL, "Failed to create stack for kernel thread.\r\n");
    /* 在内核堆中创建 PCB 时已经全部清零，因此只需要将需要的字段设置相应的值 */
    lpMainThread->lpKernelStack = (LPVOID)((DWORD)lpMainThread + PAGE_SIZE * 2); /* 主线程的内核栈顶 */
    lpMainThread->bIsIdle = FALSE;
    lpMainThread->dwPriority = 10;
    lpMainThread->nTicks = 10;
    lpMainThread->dwStatus = THREAD_READY;
    /* 段寄存器初值 */
    lpMainThread->context.dwCS = SELECTOR_KERNEL_CODE;
    lpMainThread->context.dwSS = SELECTOR_KERNEL_DATA;
    lpMainThread->context.dwDS = SELECTOR_KERNEL_DATA;
    lpMainThread->context.dwES = 0;
    lpMainThread->context.dwFS = 0;
    lpMainThread->context.dwGS = 0;
    /* 线程入口 */
    lpMainThread->context.dwEIP = (DWORD)InitializeMainThread; /* 主线程初始化函数入口地址 */
    /* 通用寄存器初值 */
    lpMainThread->context.dwESP = (DWORD)lpMainThread->lpKernelStack; /* 指向主线程内核栈栈顶 */
    lpMainThread->context.dwEBP = (DWORD)lpMainThread->lpKernelStack; /* 与主线程内核栈栈顶保持一致 */
    lpMainThread->context.dwEAX = 0;
    lpMainThread->context.dwEBX = 0;
    lpMainThread->context.dwECX = 0;
    lpMainThread->context.dwEDX = 0;
    lpMainThread->context.dwESI = 0;
    lpMainThread->context.dwEDI = 0;
    /* 标志位寄存器 */
    lpMainThread->context.dwEFLAGS = 0x0000; /* IOPL = 0 IF = 0 */
    lpMainThread->lpProcess = lpNewProcess;
    lpMainThread->dwUserStackSize = 8192; /* 主线程默认用户栈大小 */
    lpMainThread->lpPrevSibling = NULL;
    lpMainThread->lpNextSibling = NULL;
    lpNewProcess->lpThreadList = lpMainThread;
    AppendTCBToQueue(g_lpReadyTCBQueue, lpMainThread); /* 将 TCB 添加到就绪队列 */
    return lpNewProcess;
}